﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Threading;

namespace SynchronizedPlayer
{
    class Controller
    {
        public ConcurrentQueue<Command> InCommands { get; private set; }

        public NetReceiver Receiver { get; private set; }
        public NetSender Sender { get; private set; }
        private Timer _executor;

        Dictionary<Type, LinkedList<Action<object[]>>> Callbacks = new Dictionary<Type, LinkedList<Action<object[]>>>();

        private bool _work = true;

        public Controller(int listenPort = 29070, string IP = "127.0.0.1", int connectPort = 29070)
        {
            InCommands = new ConcurrentQueue<Command>();

            Setup(listenPort, IP, connectPort);            
        }

        
        public void Setup(int listenPort, string toIP, int toPort)
        {
            if (Receiver != null) Receiver.Dispose();
            Receiver = new NetReceiver(27960, InCommands);
            Sender = new NetSender("127.0.0.1", 27960);
            _executor = new Timer(new TimerCallback(ExecuteLoop), null, 0, 100);

            foreach (var type in Command.AllCommands)
            {
              Callbacks.Add(  type.Value, new LinkedList<Action<object[]>>());
            }
        }
        public void Subscribe(Type t, Action<object[]> act)
        {
            Callbacks[t].AddLast(act);
        }

        public void SendPlaylist(string currentTrack, string position, string[] items)
        {
            Sender.Enqueue(new CMDPlaylistBegin(currentTrack, position));
            foreach (var item in items)
            {
                Sender.Enqueue(new CMDPlaylistItem(item));
            }
            Sender.Enqueue(new CMDPlaylistEnd());
        }


        private void ExecuteLoop(object o)
        {
            if (!_work) _executor.Dispose();
            else {
                Command next;
                
                if (InCommands.TryDequeue(out next))
                {
                    dynamic nextcmd = next;
                    object[] execResult = Execute(nextcmd);
                    foreach (var act in Callbacks[nextcmd.GetType()])
                    {
                        act(execResult);
                    } 
                    Thread.Sleep(100);
                }
             
            }
        }

        private object[] Execute(CMDSetPosition c)
        {
            Debug.WriteLine ("Set position. hash {0} position {1}", c.Hash, c.Position);
            return new object[] {};
        }
        private object[] Execute(CMDPlaylistBegin c)
        {
            object[] result = new object[3];
            
            List<string> items = new List<string>();

            Debug.WriteLine("Playlist started. current track {0} position {1}", c.CurrentTrack, c.Position);
            Command next;
            bool end = false;
            while (!end)
            {
                while (InCommands.TryDequeue(out next) && next is CMDPlaylistItem)
                {
                    Debug.WriteLine(next.ToString());
                    items.Add((next as CMDPlaylistItem).Hash);
                }
                if (next is CMDPlaylistEnd)
                {
                    Debug.WriteLine("Hey, it ended!");
                    end = true;
                }
            }
            result[0] = Convert.ToInt32(c.CurrentTrack);
            result[1] = Convert.ToDouble(c.Position);
            result[2] = items;
            return result;
        }

        public void Dispose()
        { 
                _work = false;            
        }

    }
}
